package com.zlx.mall.basic.web.service;


import com.zlx.mall.basic.util.json.JacksonUtil;
import com.zlx.mall.basic.util.net.NetUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.Optional;
import java.util.function.Function;

/**
 * @author qingke
 **/
@Slf4j(topic = "httpServiceLogger")
@Service
public class RestTemplateImpl extends AbstractHttpService {

    @Resource(name = "traceRestTemplate")
    private RestTemplate traceRestTemplate;

    private <T, R> ResponseEntity<R> exchange(boolean save, @Nullable String groupName, RequestEntity<T> requestEntity,
                                              Function<RequestEntity<T>, ResponseEntity<R>> function) {

        log.info("> HttpService inetAddress: {}, method: {}, url: {}, requestHeaders: {}, requestBody: {}",
                NetUtil.localAddress(),
                requestEntity.getMethod(),
                requestEntity.getUrl(),
                getHeaders(requestEntity),
                getBody(requestEntity));

        Long start = System.currentTimeMillis();

        ResponseEntity<R> responseEntity = ResponseEntity.internalServerError().build();
        Long end;
        try {
            responseEntity = function.apply(requestEntity);
            end = System.currentTimeMillis();

            log.info("> HttpService inetAddress: {}, method: {}, url: {}, requestHeaders: {}, requestBody: {}, responseStatus: {}, responseHeaders: {}, responseBody: {}, {}ms",
                    NetUtil.localAddress(),
                    requestEntity.getMethod(),
                    requestEntity.getUrl(),
                    getHeaders(requestEntity),
                    getBody(requestEntity),
                    responseEntity.getStatusCodeValue(),
                    getHeaders(responseEntity),
                    getBody(responseEntity),
                    end - start);

            if (save) {
                httpLogService.safeSaveLog(groupName, start, requestEntity, end, responseEntity);
            }

        } catch (RestClientResponseException e) {
            responseEntity = ResponseEntity.status(e.getRawStatusCode()).build();
            end = System.currentTimeMillis();

            log.info("> HttpService inetAddress: {}, method: {}, url: {}, requestHeaders: {}, requestBody: {}, responseStatus: {}, responseHeaders: {}, responseBody: {}, {}ms",
                    NetUtil.localAddress(),
                    requestEntity.getMethod(),
                    requestEntity.getUrl(),
                    getHeaders(requestEntity),
                    getBody(requestEntity),
                    e.getRawStatusCode(),
                    Optional.ofNullable(e.getResponseHeaders()).map(JacksonUtil::toJsonString).orElse(StringUtils.EMPTY),
                    e.getResponseBodyAsString(),
                    end - start);

            if (save) {
                httpLogService.safeSaveLog(groupName, start, requestEntity, end, e);
            }

        } catch (Exception e) {
            log.error("> HttpService exception", e);
        }

        return responseEntity;
    }

    @Override
    public <T, R> ResponseEntity<R> exchange(@Nullable String groupName, RequestEntity<T> requestEntity, Class<R> rClass) {
        return exchange(Boolean.TRUE, groupName, requestEntity, (r) -> traceRestTemplate.exchange(r, rClass));
    }

    @Override
    public <T, R> ResponseEntity<R> exchange(@Nullable String groupName, RequestEntity<T> requestEntity, ParameterizedTypeReference<R> typeReference) {
        return exchange(Boolean.TRUE, groupName, requestEntity, (r) -> traceRestTemplate.exchange(r, typeReference));
    }

    @Override
    public <T, R> ResponseEntity<R> exchangeWithoutSave(@Nullable String groupName, RequestEntity<T> requestEntity, Class<R> rClass) {
        return exchange(Boolean.FALSE, groupName, requestEntity, (r) -> traceRestTemplate.exchange(r, rClass));
    }


}
